// --------------------------------------------------------------------------------------------------------------------
// <copyright file="VirtualMarket.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge.Plugins
{
    #region Using Directives

    using System;

    using Org.OpenTrader.Framework.Enums;
    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Events;
    using Org.OpenTrader.Framework.Forge.Interfaces;
    using Org.OpenTrader.Framework.Interfaces;

    #endregion

    /// <summary>
    /// A virtual market for some instrument, orders here are dealt as follow
    /// Send(...)
    /// - Limit : Enqueued then matched (if possible) when feed arrives (asynchronous)
    /// - FillKill : Executed synchronously (partially, completely or none) and cancelled immediately (synchronous)
    /// Cancel(...)
    /// - Limit : Dequeued 
    /// - FillKill : Not Applicable
    /// Events are sent whenever necessary (ackorder, ackcancel, executions, done, etc ...)
    /// </summary>
    public partial class VirtualMarket
    {
        private static readonly Identity Identity = Identity.Create(System.Reflection.MethodBase.GetCurrentMethod());

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="VirtualMarket"/> class.
        /// </summary>
        /// <param name="plugin">
        /// The plugin.
        /// </param>
        /// <param name="instrumentID">
        /// The instrument ID.
        /// </param>
        public VirtualMarket(OrderPlugin plugin, Guid instrumentID)
        {
            this.MarketBook = new MarketBook(5);
            this.OrderPlugin = plugin;
            this.Last = new MarketField(EMarketField.Last);
            this.LastSize = new MarketField(EMarketField.LastSize);
            this.OrderCellsQueue = new SortedArray<VirtualMarketOrder>(new VirtualMarketOrderCellComparer());
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets Last.
        /// </summary>
        public MarketField Last { get; private set; }

        /// <summary>
        /// Gets Last.
        /// </summary>
        public MarketField LastSize { get; private set; }

        /// <summary>
        /// Gets MarketBook.
        /// </summary>
        public IMarketBook MarketBook { get; private set; }

        /// <summary>
        /// Gets OrderCellsQueue.
        /// </summary>
        public ISortedArray<VirtualMarketOrder> OrderCellsQueue { get; private set; }

        /// <summary>
        /// Gets OrderPlugin.
        /// </summary>
        public OrderPlugin OrderPlugin { get; private set; }

        #endregion

        #region Public Methods

        /// <summary>
        /// Send Cancel order query
        /// </summary>
        /// <param name="order">
        /// The order.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public Status Cancel(IOrder order, RemotableEventHandler<OrderEventArgs> handler)
        {
            var vmo = new VirtualMarketOrder(order, handler);
            if (!this.OrderCellsQueue.Contains(vmo))
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Cancel : cannot cancel order, unknown order"));
                return Status.Failure(Identity, "Cancel: This order is not known");
            }

            lock (this.OrderCellsQueue)
            {
                // The real one is there
                var index = this.OrderCellsQueue.IndexOf(vmo);

                // Get it
                vmo = this.OrderCellsQueue[index];

                // Set to cancelling
                vmo.Cancelling = true;
            }

            return Status.Success(Identity);
        }

        /// <summary>
        /// Send Modify order query
        /// </summary>
        /// <param name="oldOrder">
        /// The oldOrder.
        /// </param>
        /// <param name="newOrder">
        /// The newOrder.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public Status Modify(IOrder oldOrder, IOrder newOrder, RemotableEventHandler<OrderEventArgs> handler)
        {
            var vmo = new VirtualMarketOrder(oldOrder, handler);
            if (!this.OrderCellsQueue.Contains(vmo))
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Modify : cannot modify order, unknown order"));
                return Status.Failure(Identity, "Modify: This order is not known");
            }

            lock (this.OrderCellsQueue)
            {
                // The real one is there
                var index = this.OrderCellsQueue.IndexOf(vmo);

                // Get it
                vmo = this.OrderCellsQueue[index];

                // Set to modifying
                vmo.Modifying = true;
                vmo.NewPrice = newOrder.Price;
                vmo.NewQuantity = newOrder.Quantity;
            }

            return Status.Success(Identity);
        }

        /// <summary>
        /// Call back from the feed service
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="args">
        /// The feed event args.
        /// </param>
        public void OnFeedEvent(object sender, FeedEventArgs args)
        {
            if (args is FeedEventArgs.MarketBookUpdate)
            {
                var mbargs = args as FeedEventArgs.MarketBookUpdate;

                // Update the book
                this.MarketBook.HandleUpdate(mbargs);

                // Update pending limit orders 
                this.LimitOrdersUpdatePending();
            }
            else if (args is FeedEventArgs.MarketFieldUpdate)
            {
                // to be completed
                var mtargs = args as FeedEventArgs.MarketFieldUpdate;

                var gotLast = false;
                var gotLastSize = false;
                foreach (var marketField in mtargs.Fields)
                {
                    if (marketField.FieldName == EMarketField.Last)
                    {
                        // Got a Last (price)
                        this.Last = marketField;
                        gotLast = true;
                    }

                    if (marketField.FieldName == EMarketField.LastSize)
                    {
                        // Got a Last (size)
                        this.LastSize = marketField;
                        gotLastSize = true;
                    }

                    if (gotLast && gotLastSize)
                    {
                        // Match limit orders if possible
                        this.LimitOrdersTryMatch();
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// Send new order
        /// </summary>
        /// <param name="order">
        /// The order.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public Status Send(IOrder order, RemotableEventHandler<OrderEventArgs> handler)
        {
            // Now handle things according order type
            switch (order.Type)
            {
                case EOrderType.FillOrKill:
                    this.MatchFillOrKillSynchronously(order, this.MarketBook, handler);
                    break;

                case EOrderType.Limit:
                    this.EnqueueLimitOrderForAsynchronousMatching(order, handler);
                    break;
            }

            return Status.Success(Identity);
        }

        #endregion

        #region Methods

        /// <summary>
        /// CanExecute : return true if slippage&gt; SlippageFactor.
        /// </summary>
        /// <returns>
        /// The can execute.
        /// </returns>
        private bool CanExecute()
        {
            var rndm = new Random();
            var d = rndm.NextDouble();
            if (d >= (double)this.OrderPlugin.Configuration["Slipage"])
            {
                return true;
            }

            return false;
        }

        #endregion
    }
}